#ifndef RTPPARSE_PACKET_H
#define RTPPARSE_PACKET_H

#include <map>

namespace Rtp {
    namespace Rtcp {

        constexpr size_t BufferSize { 65536 };
        extern uint8_t Buffer[BufferSize];

        constexpr uint16_t MaxAudioIntervalMs { 5000 };
        constexpr uint16_t MaxVideoIntervalMs { 1000 };

        enum class Type : uint8_t {
            SR = 200,
            RR = 201,
            SDES = 202,
            BYE = 203,
            APP = 204,
            RTPFB = 205,
            PSFB = 206,
            XR = 207
        };
        
        class Packet {
        public:
            // Struct for RTCP common header. */
            struct CommonHeader {
                uint8_t count : 5;
                uint8_t padding : 1;
                uint8_t version : 2;
                uint8_t packetType;
                uint16_t length;
            };
        public:
            static const size_t CommonHeaderSize { 4 };
            static bool IsRtcp(const uint8_t* data, size_t len) {
                auto *header = const_cast<CommonHeader*>(reinterpret_cast<const CommonHeader*>(data));
                return (len >= CommonHeaderSize) && (data[0] > 127 && data[0] < 192) &&
                (header->version == 2) &&
                (header->packetType >= 192 && header->packetType <= 223);
            };

            static Packet* Parse(const uint8_t* data, size_t len);
            static const std::string& Type2String(Type type);
        private:
            static std::map<Type, std::string> type2String;
        public:
            explicit Packet(Type type): type(type) {}
            
            explicit Packet(const CommonHeader* commonHeader) {
                this->type = static_cast<Type>(commonHeader->packetType);
                this->header = const_cast<CommonHeader*>(commonHeader);
            }

            virtual ~Packet() = default;
            
            void SetNext(Packet* packet) {
                this->next = packet;
            }

            Packet* GetNext() const {
                return this->next;
            }

            const uint8_t* GetData() const {
                return reinterpret_cast<const uint8_t*>(this->header);
            }

        public:
            virtual void Dump() const = 0;
            virtual size_t Serialize(uint8_t* buffer) = 0;
            virtual Type GetType() const {
                return this->type;
            }

            virtual size_t GetCount() const {
                return 0u;
            }

            virtual size_t GetSize() const = 0;
        private:
            Type type;
            Packet *next { nullptr };
            CommonHeader *header{ nullptr };
        };
    }
}

#endif